package fr.pud.client.model;

import java.io.Serializable;

import org.openstreetmap.gui.jmapviewer.Coordinate;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import fr.pud.client.i18n.LanguagesConstants;
import fr.pud.client.view.MainFrame;
import fr.pud.utils.Conversion;
import fr.pud.utils.DOMParser;
import fr.pud.utils.IdOSM;

/**
 * The representation of an address represented by the GPS-coordinates, the
 * address and the osm id. Apr 9, 2012 - OSE.
 * @author Simon Devineau, Adrien Hierry, Leopold Houdin, Pierre Reliquet Ecole
 *         des Mines de Nantes Address.java
 */
public class Address implements Serializable {
    /**
     * The serial UID for serialization.
     */
    private static final long   serialVersionUID   = -6687241485445335132L;
    /**
     * One default coordinate : Nantes, France.
     */
    public static final Address DEFAULT_COORDINATE = new Address(
                                                           new Coordinate(
                                                                   MainFrame.DEFAULT_LATITUDE,
                                                                   MainFrame.DEFAULT_LONGITUDE));
    /**
     * The address if an error occurs
     */
    public static final String  ERROR_ADDRESS      = LanguagesConstants.LANGUAGE
                                                           .getString(LanguagesConstants.UNKNOWN_ADDRESS)
                                                           + " "
                                                           + LanguagesConstants.LANGUAGE
                                                                   .getString(LanguagesConstants.OR)
                                                           + " "
                                                           + LanguagesConstants.LANGUAGE
                                                                   .getString(LanguagesConstants.SERVER_UNREACHABLE_NOMINATIM);

    public static boolean isAddressCorrect(Address a) {
        if (a.getAddressName().equals(ERROR_ADDRESS)) {
            return false;
        }
        return true;
    }

    /**
     * The coordinate associated to the address.
     */
    private Coordinate coordinate;
    /**
     * The address
     */
    private String     addressName;
    /**
     * The osm id.
     */
    private long       osmId;

    /**
     * Default constructor, each parameter is put to zero or "".
     */
    public Address() {
        this.coordinate = new Coordinate(0.0, 0.0);
        this.addressName = ERROR_ADDRESS;
        this.osmId = 0;
    }

    /**
     * Constructor<br />
     * The parameters are obtained by geocoding.
     * @param a
     *            the address
     */
    public Address(Address a) {
        this.coordinate = new Coordinate(a.getCoordinate().getLat(), a
                .getCoordinate().getLon());
        this.osmId = a.getOsmId();
        this.addressName = a.getAddressName();
    }

    /**
     * Create a new address from a GPS coordinate. The address is calculated by
     * reverse geocoding and the ID osm is obtained by this reverse geocoding.
     * @param aCoordinate
     *            , the coordinate.
     */
    public Address(Coordinate aCoordinate) {
        this.coordinate = aCoordinate;
        IdOSM id = Conversion.reverseGeocoding(this.coordinate);
        this.addressName = Conversion.adressSplittingForJLabel(id.getAddress());
        this.osmId = id.getOsmId();
    }

    /**
     * Create a new address from a latitude and a longitude. The address is
     * calculated by reverse geocoding and the ID osm is obtained by this
     * reverse geocoding.
     * @param lat
     *            the latitude.
     * @param lon
     *            the longitude.
     */
    public Address(double lat, double lon) {
        this.coordinate = new Coordinate(lat, lon);
        IdOSM id = Conversion.reverseGeocoding(this.coordinate);
        this.addressName = Conversion.adressSplittingForJLabel(id.getAddress());
        this.osmId = id.getOsmId();
    }

    /**
     * The complete constructor where each parameter is specified.
     * @param lat
     *            the latitude.
     * @param lon
     *            the longitude
     * @param idOsm
     *            the id osm
     * @param address
     *            the address.
     */
    public Address(double lat, double lon, long idOsm, String address) {
        this.coordinate = new Coordinate(lat, lon);
        this.addressName = address;
        this.osmId = idOsm;
    }

    /**
     * Constructor
     * @param anAddress
     *            the address
     * @param aCoordinate
     *            the coordinate.
     */
    public Address(String anAddress, Coordinate aCoordinate) {
        this.addressName = Conversion.adressSplittingForJLabel(anAddress);
        this.coordinate = aCoordinate;
    }

    /**
     * Constructor
     * @param anAddress
     *            the address
     * @param aCoordinate
     *            the coordinate
     * @param id
     *            the id.
     */
    public Address(String anAddress, Coordinate aCoordinate, long id) {
        this.addressName = Conversion.adressSplittingForJLabel(anAddress);
        this.coordinate = aCoordinate;
        this.osmId = id;
    }

    /**
     * @param obj
     *            the object to check the equality
     * @return true if the obj is instanceof Address and if is equal to this.
     */
    @Override
    public boolean equals(Object obj) {
        boolean toReturn = false;
        if (obj instanceof Address) {
            toReturn = ((Address) obj).getCoordinate().getLat() == this
                    .getCoordinate().getLat()
                    && ((Address) obj).getCoordinate().getLon() == this
                            .getCoordinate().getLon();
            return toReturn;
        }
        else if (obj instanceof Coordinate) {
            return ((Coordinate) obj).getLat() == this.getCoordinate().getLat()
                    && ((Coordinate) obj).getLon() == this.getCoordinate()
                            .getLon();
        }
        else {
            return toReturn;
        }
    }

    /**
     * @return the address.
     */
    public String getAddressName() {
        return this.addressName;
    }

    /**
     * @return the coordinate.
     */
    public Coordinate getCoordinate() {
        return this.coordinate;
    }

    /**
     * @return the osm id.
     */
    public long getOsmId() {
        return this.osmId;
    }

    /**
     * @return the hashcode.
     */
    @Override
    public int hashCode() {
        return (int) this.getOsmId() + (int) this.getCoordinate().getLat()
                + (int) this.getCoordinate().getLon()
                + this.getAddressName().hashCode();
    }

    /**
     * @return a String representation.
     */
    @Override
    public String toString() {
        if (this.addressName.equals(ERROR_ADDRESS)) {
            return ERROR_ADDRESS;
        }
        else {
            return this.addressName ;
        }
    }

    /**
     * @param d
     *            the document where the xml representation is going to be bind.
     * @return an xml representation of the address.
     */
    public Element toXML(Document d) {
        return this.toXML(d, "address");
    }

    /**
     * @param d
     *            the document where the xml is going to be bind.
     * @param name
     *            the name of the root element
     * @return the xml representation.
     */
    public Element toXML(Document d, String name) {
        Element root = d.createElement(name);
        DOMParser
                .getElementAppended(d, "location", this.getAddressName(), root);
        Element coordinate = d.createElement("coordinate");
        DOMParser.getElementAppended(d, "latitude", ""
                + this.getCoordinate().getLat(), coordinate);
        DOMParser.getElementAppended(d, "longitude", ""
                + this.getCoordinate().getLon(), coordinate);
        root.appendChild(coordinate);
        DOMParser.getElementAppended(d, "osmID", "" + this.getOsmId(), root);
        return root;
    }
}
